function Database(uri) {
	this._saveable = false;	
	if (uri !== undefined) {
		this._ds = Database.rdf.GetDataSourceBlocking(uri);
		this._saveable = true;
	}
	else {
		this._ds = CC[Database.INMEM_DATASOURCE_CID].createInstance(CI.nsIRDFDataSource);
	}

	this.sitesList = new DatabaseEntry(Database.sitesRoot, this._ds);
	this.datesList = new DatabaseEntry(Database.datesRoot, this._ds);
	this.totalsList = new SiteEntry(Database.totalsRoot, this._ds);
}

Database.RDFSERVICE_CID = "@mozilla.org/rdf/rdf-service;1";
Database.RDFCONTAINERUTILS_CID = "@mozilla.org/rdf/container-utils;1";
Database.INMEM_DATASOURCE_CID = "@mozilla.org/rdf/datasource;1?name=in-memory-datasource";

Database.rdf = CC[Database.RDFSERVICE_CID].getService(CI.nsIRDFService);
Database.rdfContainerUtils = CC[Database.RDFCONTAINERUTILS_CID].getService(CI.nsIRDFContainerUtils);

Database.dcNS = "http://purl.org/dc/elements/1.1/";
Database.ncNS = "http://home.netscape.com/NC-rdf#";
Database.tkctrlNS = "http://code.google.com/p/takecontrol/";
Database.tkctrlRdfNS = Database.tkctrlNS + "rdf#";
Database.tkctrlSitesNS = Database.tkctrlNS + "sites/";
Database.tkctrlDatesNS = Database.tkctrlNS + "dates/";

Database.hostProp = Database.rdf.GetResource(Database.tkctrlRdfNS + "SiteId");
Database.durationProp = Database.rdf.GetResource(Database.tkctrlRdfNS + "Duration");
Database.dateProp = Database.rdf.GetResource(Database.tkctrlRdfNS + "Date");
Database.dateStringProp = Database.rdf.GetResource(Database.dcNS + "date");
Database.sitesRoot = Database.rdf.GetResource(Database.tkctrlRdfNS + "SitesRoot");
Database.datesRoot = Database.rdf.GetResource(Database.tkctrlRdfNS + "DatesRoot");
Database.totalsRoot = Database.rdf.GetResource(Database.tkctrlRdfNS + "TotalsRoot");

Database.dsUpdate = function DB_dsUpdate(datasource, subject, property, newRDFNode) {
	//helper method for changing one existing value to another
	var oldRDFNode = datasource.GetTarget(subject, property, true);
	if (oldRDFNode instanceof CI.nsIRDFLiteral || oldRDFNode instanceof CI.nsIRDFInt) {		
		datasource.Change(subject, property, oldRDFNode, newRDFNode);
	}
}

Database._generateSiteResource = function DB__generateSiteResource(siteId) {
	return Database._generateResource(Database.tkctrlSitesNS, siteId);
}

Database._generateDateResource = function DB__generateDateResource(date) {
	return Database._generateResource(Database.tkctrlDatesNS, date);
}

Database._generateResource = function DB__generateResource(root, id) {
	return Database.rdf.GetResource(root + id);
}

Database.prototype.dsAvailable = function() {
	if (this._ds && this._ds.GetSource !== undefined)
		return true;
	else
		return false;
}

/** a SessionEntry is an anonymous node that has date and duration properties */
Database.prototype._createSessionEntry = function(date, duration) {
	var sessionEntryRes = Database.rdf.GetAnonymousResource();
	var dateRes = this._getDateResource(date);
	var durationLit = Database.rdf.GetIntLiteral(duration);

	this._ds.Assert(sessionEntryRes, Database.dateProp, dateRes, true);
	this._ds.Assert(sessionEntryRes, Database.durationProp, durationLit, true);

	return sessionEntryRes;
}

Database.prototype._createSiteEntry = function DB__createSiteEntry(siteId) {
	var siteRes = Database._generateSiteResource(siteId);
	var hostLit = Database.rdf.GetLiteral(siteId);
	
	this._ds.Assert(siteRes, Database.hostProp, hostLit, true);
	this.sitesList.add(siteRes);
	
	return new SiteEntry(siteRes, this._ds);
}

Database.prototype._getDateResource = function DB__getDateResource(date) {
	var dateRes = Database._generateDateResource(date);
	
	if (!this.datesList.contains(dateRes)) {
		var dateLit = Database.rdf.GetLiteral(date);
		this._ds.Assert(dateRes, Database.dateStringProp, dateLit, true);
		this.datesList.add(dateRes);
	}
	return dateRes;
}

Database.prototype._siteExists = function DB__siteExists(siteId) {
	return this.sitesList.contains(Database._generateSiteResource(siteId));
}

Database.prototype._getSite = function DB__getSite(siteId) {
	return new SiteEntry(Database._generateSiteResource(siteId), this._ds);
}

Database.prototype.getSiteDuration = function DB_getSiteDuration(siteId, date) {
	var site;
	var duration = 0;
	if (this._siteExists(siteId)) {		
		site = this._getSite(siteId);
		return duration = site.getSessionDuration(date);
	}

	return duration;
}

Database.prototype.updateOrCreateSiteSession = function DB_updateOrCreateSiteSession(siteId, date, duration) {
	var site, session;

	if (this._siteExists(siteId)) {		
		site = this._getSite(siteId);
	}
	else {
		site = this._createSiteEntry(siteId);
	}
	
	if (site.sessionExists(date)) {
		site.updateSessionDuration(date, duration)
	}
	else {
		session = this._createSessionEntry(date, duration);
		site.add(session);
	}
}

Database.prototype.getDailyTotal = function DB_getDailyTotal(date) {
	return this.totalsList.getSessionDuration(date);
}

Database.prototype.updateOrCreateDailyTotal = function DB_updateOrCreateDailyTotal(date, duration) {
	if (this.totalsList.sessionExists(date)) {
		this.totalsList.updateSessionDuration(date, duration)
	}
	else {
		session = this._createSessionEntry(date, duration);
		this.totalsList.add(session);
	}
	//TODO reduce duplication with the method above.
}

Database.prototype.save = function DB_save() {
	if (this._saveable) {
		this._ds.QueryInterface(CI.nsIRDFRemoteDataSource);
		this._ds.Flush();
	}
}
